﻿/*
	wp8libflac project
	© Alovchin, 2014
*/

/*
	These functions were taken and adapted from FlacBox project:
	http://flacbox.codeplex.com/
*/

using namespace wp8::Interop;

#pragma once

static inline uint32 PackSample8(const int* const data[], uint32 blockSize, uint32 channels, uint8 *buffer)
{
	int k = 0;
	for (uint32 i = 0; i < blockSize; i++)
	{
		for (uint32 j = 0; j < channels; j++)
		{
			buffer[k++] = (uint8)(data[j][i] + 0x80);
		}
	}
	return k;
}

static inline uint32 PackSample16(const int* const data[], uint32 blockSize, uint32 channels, uint8 *buffer)
{
	int k = 0;
	for (uint32 i = 0; i < blockSize; i++)
	{
		for (uint32 j = 0; j < channels; j++)
		{
			buffer[k++] = (uint8)(data[j][i] & 0xFF);
			buffer[k++] = (uint8)((data[j][i] >> 8) & 0xFF);
		}
	}
	return k;
}

static inline uint32 PackSample24(const int* const data[], uint32 blockSize, uint32 channels, uint8 *buffer)
{
	int k = 0;
	for (uint32 i = 0; i < blockSize; i++)
	{
		for (uint32 j = 0; j < channels; j++)
		{
			buffer[k++] = (uint8)(data[j][i] & 0xFF);
			buffer[k++] = (uint8)((data[j][i] >> 8) & 0xFF);
			buffer[k++] = (uint8)((data[j][i] >> 16) & 0xFF);
		}
	}
	return k;
}

static inline uint32 PackSample32(const int* const data[], uint32 blockSize, uint32 channels, uint8 *buffer)
{
	int k = 0;
	for (uint32 i = 0; i < blockSize; i++)
	{
		for (uint32 j = 0; j < channels; j++)
		{
			buffer[k++] = (uint8)(data[j][i] & 0xFF);
			buffer[k++] = (uint8)((data[j][i] >> 8) & 0xFF);
			buffer[k++] = (uint8)((data[j][i] >> 16) & 0xFF);
			buffer[k++] = (uint8)((data[j][i] >> 24) & 0xFF);
		}
	}
	return k;
}

static inline uint32 PackSample(const int* const data[], uint32 blockSize, uint32 channels, IBuffer^ buffer, uint32 bitsPerSample)
{
	if (buffer->Capacity < blockSize * channels * (bitsPerSample / 8))
		throw ref new InvalidArgumentException("Buffer too small.");

	uint8 *pBuffer = BufferExtensions::GetUnderlyingArray(buffer);

	switch (bitsPerSample)
	{
	case 8:
		return PackSample8(data, blockSize, channels, pBuffer);
	case 16:
		return PackSample16(data, blockSize, channels, pBuffer);
	case 24:
		return PackSample24(data, blockSize, channels, pBuffer);
	case 32:
		return PackSample32(data, blockSize, channels, pBuffer);
	default:
		throw ref new InvalidArgumentException("Invalid bits per sample count.");
	}
}

#if USE_WAVEHEADER

static inline const Array<uint8>^ CreateWaveHeader(FLAC__StreamMetadata_StreamInfo stream_info, int64 *streamLength)
{
	const int fmtChunkOffset = 12;
	const int dataChunkOffset = 36;
	const int waveHeaderSize = dataChunkOffset + 8;

	int bytesPerInterChannelSample = (stream_info.channels * stream_info.bits_per_sample) >> 3;
	int64 dataLength = stream_info.total_samples * bytesPerInterChannelSample;
	int64 totalStreamLength = dataLength + waveHeaderSize;
	*streamLength = totalStreamLength;

	std::array<uint8, waveHeaderSize> waveHeader = { 0 };
	waveHeader[0] = (uint8)'R';
	waveHeader[1] = (uint8)'I';
	waveHeader[2] = (uint8)'F';
	waveHeader[3] = (uint8)'F';

	uint32 dataOffset = (uint32)totalStreamLength - 8;
	std::memcpy(&waveHeader[4], &dataOffset, 4);
	waveHeader[8] = (uint8)'W';
	waveHeader[9] = (uint8)'A';
	waveHeader[10] = (uint8)'V';
	waveHeader[11] = (uint8)'E';

	waveHeader[fmtChunkOffset + 0] = (uint8)'f';
	waveHeader[fmtChunkOffset + 1] = (uint8)'m';
	waveHeader[fmtChunkOffset + 2] = (uint8)'t';
	waveHeader[fmtChunkOffset + 3] = (uint8)' ';
	waveHeader[fmtChunkOffset + 4] = 16;
	waveHeader[fmtChunkOffset + 8] = 1;
	waveHeader[fmtChunkOffset + 10] = (uint8)stream_info.channels;
	std::memcpy(&waveHeader[fmtChunkOffset + 12], &stream_info.sample_rate, 4);
	int bytesPerSecond = stream_info.sample_rate * bytesPerInterChannelSample;
	std::memcpy(&waveHeader[fmtChunkOffset + 16], &bytesPerSecond, 4);
	uint16 bytesPerInterChannelSampleShort = (uint16)bytesPerInterChannelSample;
	std::memcpy(&waveHeader[fmtChunkOffset + 20], &bytesPerInterChannelSampleShort, 2);
	uint16 bitsPerSampleShort = (uint16)stream_info.bits_per_sample;
	std::memcpy(&waveHeader[fmtChunkOffset + 22], &bitsPerSampleShort, 2);

	waveHeader[dataChunkOffset + 0] = (uint8)'d';
	waveHeader[dataChunkOffset + 1] = (uint8)'a';
	waveHeader[dataChunkOffset + 2] = (uint8)'t';
	waveHeader[dataChunkOffset + 3] = (uint8)'a';
	uint32 unsignedDataLength = (uint32)dataLength;
	std::memcpy(&waveHeader[dataChunkOffset + 4], &unsignedDataLength, 4);

	return ref new Array<uint8>(&waveHeader[0], waveHeaderSize);
}

#endif
